/*
 * MIT License
 *
 * Copyright (c) 2020 wen.gu <454727014@qq.com>
 *
 * Permission is hereby granted, free of charge, to any person obtaining a copy
 * of this software and associated documentation files (the "Software"), to deal
 * in the Software without restriction, including without limitation the rights
 * to use, copy, modify, merge, publish, distribute, sublicense, and/or sell
 * copies of the Software, and to permit persons to whom the Software is
 * furnished to do so, subject to the following conditions:
 *
 * The above copyright notice and this permission notice shall be included in all
 * copies or substantial portions of the Software.
 *
 * THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR
 * IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY,
 * FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE
 * AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER
 * LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM,
 * OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE
 * SOFTWARE.
 */

/***************************************************************************
 * Name: service_skeleton.h
 *
 * Purpose: service skeleton class implementation for service side, this class
 *          will be used for code generator 
 *
 * Developer:
 *   wen.gu , 2021-09-15
 *
 * TODO:
 *
 ***************************************************************************/

/******************************************************************************
 **    INCLUDES
 ******************************************************************************/
#ifndef __ICPP_COM_SERVICE_SKELETON_H__
#define __ICPP_COM_SERVICE_SKELETON_H__
#include <functional>
#include <vector>
#include <memory>

#include "icpp/com/types.h"
#include "icpp/com/message.h"

#include "icpp/com/response.h"
#include "icpp/com/template_utils.h"
#include "icpp/com/message_broker.h"
/******************************************************************************
 **    MACROS
 ******************************************************************************/


/******************************************************************************
 **    TYPE DEFINITIONS
 ******************************************************************************/
namespace icpp
{
namespace com
{


/******************************************************************************
 **    CLASSES/FUNCTIONS DEFINITIONS
 ******************************************************************************/
class COM_CLASS ServiceSkeleton
{
public:
    using IcppErrc = core::IcppErrc;
    using MessagePtr = Message::MessagePtr;
    using SerializerPtr = Serializer::SerializerPtr;
    using DeserializerPtr = Deserializer::DeserializerPtr;
    using MessageHandler = MessageBroker::MessageHandler; 
public:
    ServiceSkeleton(const std::string& service_name, uint8_t interface_version = 0);
    virtual ~ServiceSkeleton();

public:
    virtual IcppErrc offerService();
    virtual IcppErrc stopService();    

public:
    template<typename ReturnType, typename ...Args>
    void registerMethodHandler(MethodId method_id, std::function<Response<ReturnType>(Args... args)> method_handler)
    {
        registerMessageHandler(MessageType::kRequest, method_id, [this, method_handler](Message::MessagePtr msg_ptr)
        {            
            if (msg_ptr->payload() == nullptr)
            {
                this->onReplyError(msg_ptr, core::IcppErrc::BadParameter);
                return ;
            }
        
            std::tuple<Args...> params;
            core::IcppErrc errc = this->onParseParam(msg_ptr->payload(), params);
            if (errc != core::IcppErrc::OK)
            {
                this->onReplyError(msg_ptr, errc);
                return;
            }

            Response<ReturnType> ret = Apply(method_handler, params);
            this->onReply(msg_ptr, std::move(ret));

        });
    }

    template<typename ReturnType>
    void registerMethodHandlerNoParam(MethodId method_id, std::function<Response<ReturnType>(void)> method_handler)
    {
        registerMessageHandler(MessageType::kRequest, method_id, [this, method_handler](Message::MessagePtr msg_ptr)
        {
            Response<ReturnType> ret = method_handler();
            this->onReply(msg_ptr, std::move(ret));
        });
    }

    template<typename ...Args>
    void registerMethodHandlerNoReturn(MethodId method_id, std::function<void(Args... args)> method_handler)
    {
        registerMessageHandler(MessageType::kRequestNoReturn, method_id, [this, method_handler](Message::MessagePtr msg_ptr)
        {
            std::tuple<Args...> params;
            core::IcppErrc errc = this->onParseParam(msg_ptr->payload(), params);
            if (errc != core::IcppErrc::OK)
            {
                /** todo something */
                return;
            }

            Apply(method_handler, params);
        });
    }

protected:
    template<typename ...Args>
    core::IcppErrc onParseParam(PayloadPtr payload_ptr, const std::tuple<Args...>& params)
    {
        Deserializer::DeserializerPtr desp = newDeserializer(payload_ptr);

        if (desp)
        {
            bool ret = TupleParser<std::tuple<Args...>, sizeof ...(Args)>::Parse(desp, params);
            return ret ? core::IcppErrc::OK : core::IcppErrc::DeserializeFailed;
        }        

        return core::IcppErrc::InsufficientResources;        
    }

    template<typename ReturnType>
    void onReply(Message::MessagePtr msg_ptr, Response<ReturnType>&& reply)
    {
        msg_ptr->set_payload(nullptr);
        msg_ptr->set_error_code(core::IcppErrc::OK);
        Result<ReturnType> res = reply.getResult();
        if (res.hasValue())
        {
            if (std::is_same<ReturnType, void>::value == false)
            {
                Serializer::SerializerPtr ser_ptr = newSerializer();
                if (ser_ptr->serialize(res.value()))
                {
                    msg_ptr->set_payload(ser_ptr->payload());
                }
                else
                {
                    msg_ptr->set_error_code(core::IcppErrc::SerializeFailed);
                }                                
            }
        }
        else
        {
            msg_ptr->set_error_code(res.error());
        }

        msg_ptr->set_type(MessageType::kResponse);
        
        sendMessage(msg_ptr);        
    }

    void onReplyError(Message::MessagePtr msg_ptr, core::IcppErrc errc)
    {
        msg_ptr->set_payload(nullptr);
        msg_ptr->set_error_code(errc);
        msg_ptr->set_type(MessageType::kResponse);        
        sendMessage(msg_ptr);             
    }

protected: /** for message: create, serialize, deserialize */
    
    MessagePtr newMessage(MessageType type, MessageId message_id, PayloadPtr payload_ptr);
    SerializerPtr newSerializer();
    DeserializerPtr newDeserializer(PayloadPtr payload);
    IcppErrc sendMessage(MessagePtr msg_ptr);
    /** type maybe: requst, no return request, property set/get */
    IcppErrc registerMessageHandler(MessageType type, MessageId method_id, MessageHandler method);

private:
    class Impl;
    std::unique_ptr<Impl> impl_;


};


} /** namespace com */
} /** namespace icpp */

#endif /** !__ICPP_COM_SERVICE_SKELETON_H__ */

